#include <stdio.h>
#include <SDL2/SDL.h>
#include "wl_ext_os.h"
#include "wl_ext_ringbuffer.h"
#include "wl_ext_player_pcm.h"

typedef struct {
    wl_ext_player_callback callback;
    void* pContext;
    SDL_AudioDeviceID handle;
    ring_buffer_hdl hdl;
} wl_ext_player_handle_impl;


static int global_sampleRate = 16000;

/**
 * init player
 * 
 * @param sampleRate sample rate
 * @return 0 success, < 0 fail
 */
int wl_ext_player_init(int sampleRate) {
    int result = 0;
    global_sampleRate = sampleRate;
    printf("sampleRate: %d\n", sampleRate);
    if (!(SDL_WasInit(0) & SDL_INIT_AUDIO)) {
        printf("SDL was not init\n");
        if (SDL_Init(SDL_INIT_AUDIO) != 0) {
            printf("SDL_Init fail, %s", SDL_GetError());
            result = -1;
        }else {
            printf("SDL_Init success\n");
        }
    }else {
        printf("SDL was init\n");
    }

    return result;

}

/**
 * deinit player
 * 
 * @return 0 success, < 0 fail
 */
int wl_ext_player_deinit(void) {
    if (SDL_WasInit(0) & SDL_INIT_AUDIO) {
        SDL_QuitSubSystem(SDL_INIT_AUDIO);
    }
    return 0;
}

/**
 * start player
 * 
 * @param pHandle player handle pointer, success it will return player handle, fail it will return NULL
 * @param pContext user context
 * @param callback callback
 * 
 * @return 0 success, < 0 fail
 */


static void SDLCALL sdl_play_audio_callback(void* userdata, Uint8* stream, int len) {
    wl_ext_player_handle_impl* player = (wl_ext_player_handle_impl*)userdata;
    int ret = 0;

    int bufferLen = ring_buffer_get_count(player->hdl);
    memset(stream, 0, len);
    if (bufferLen > 0) {
        if (bufferLen > len) {
            ret = ring_buffer_read(player->hdl, stream, len);
        }else {
            ret = ring_buffer_read(player->hdl, stream, bufferLen);
            player->callback(player, WL_EXT_PLAYER_DATA_DRAIN, player->pContext);
        }
    }else {
        player->callback(player, WL_EXT_PLAYER_DATA_DRAIN, player->pContext);
    }
}

wl_ext_player_handle wl_ext_player_start(void *pContext, wl_ext_player_callback callback) {
    wl_ext_player_handle_impl* player = (wl_ext_player_handle_impl*)malloc(sizeof(wl_ext_player_handle_impl));
    if (NULL == player) {
        printf("malloc fail");
        return NULL;
    }
    ring_buffer_init(&player->hdl, malloc(10240), 10240);
    player->callback = callback;
    player->pContext = pContext;
    SDL_AudioSpec    inspec, outspec;
    memset(&inspec, 0, sizeof(SDL_AudioSpec));
    memset(&outspec, 0, sizeof(SDL_AudioSpec));

    inspec.format   = AUDIO_U16LSB;
    inspec.channels = 1;
    inspec.freq     = global_sampleRate;
    inspec.samples  = global_sampleRate / 50; // 20ms
    inspec.callback = sdl_play_audio_callback;
    inspec.userdata = player;
    int count       = SDL_GetNumAudioDevices(SDL_FALSE);
    for (int i = 0; i < count; i++) {
       // SDL_Delay(100);
        printf("name:%s", SDL_GetAudioDeviceName(i, SDL_FALSE));
        SDL_CloseAudioDevice(i);
    }
    player->handle = SDL_OpenAudioDevice(
        SDL_GetAudioDeviceName(0, SDL_FALSE), SDL_FALSE, &inspec, &outspec, SDL_AUDIO_ALLOW_ANY_CHANGE);
    if (player->handle > 0) {
        SDL_PauseAudioDevice(player->handle, 1);
        SDL_PauseAudioDevice(player->handle, 0);

        if (player->callback) {
            player->callback(player, WL_EXT_PLAYER_STARTED, player->pContext);
        }
    } else {
        if (player->callback) {
            player->callback(player, WL_EXT_PLAYER_ERROR, player->pContext);
        }
        printf("SDL_OpenAudioDevice fail, %s\n", SDL_GetError());
        return NULL;
    }
    
    return (wl_ext_player_handle)player;
}

/**
 * stop player
 * 
 * @param handle player handle
 *
 * @return 0 success, < 0 fail
 */
int wl_ext_player_stop(wl_ext_player_handle handle) {
    wl_ext_player_handle_impl* player = (wl_ext_player_handle_impl*)handle;
    SDL_PauseAudioDevice(player->handle, 1);
    SDL_CloseAudioDevice(player->handle);
    ring_buffer_deinit(player->hdl);

    if (player->callback) {
        player->callback(player, WL_EXT_PLAYER_END, player->pContext);
    }

    free(player);
    return 0;
}

/**
 * feed data to player, it should drived by timer with 10ms also, or while callback get WL_EXT_PLAYER_DATA_DRAIN event,
 * it should be called to provide more pcm data.
 * 
 * @param handle player handle
 * @param pPcmData pcm data in byte format
 * @param pcmLen  bytes of pcm data 
 * @return > 0, means bytes actually read, < 0 means error
 */
int wl_ext_player_feed_data(wl_ext_player_handle handle, unsigned char *pPcmData, unsigned int pcmLen) {
    wl_ext_player_handle_impl* player = (wl_ext_player_handle_impl*)handle;
    return ring_buffer_write(player->hdl, pPcmData, pcmLen);
}

/**
 * pause player
 * 
 * @param handle player handle
 * 
 * @return 0 success, < 0 fail
 */
int wl_ext_player_pause(wl_ext_player_handle handle) {
    wl_ext_player_handle_impl* player = (wl_ext_player_handle_impl*)handle;
    SDL_PauseAudioDevice(player->handle, 1);
    return 0;
}

/**
 * resume player
 * 
 * @param handle player handle
 * 
 * @return 0 success, < 0 fail  
 */
int wl_ext_player_resume(wl_ext_player_handle handle) {
    wl_ext_player_handle_impl* player = (wl_ext_player_handle_impl*)handle;
    SDL_PauseAudioDevice(player->handle, 0);
    return 0;
}
